package com.swak.boot.config.consistency.tcc;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import com.swak.annotation.Tcc;
import com.swak.consistency.core.proxy.ProxyFactory;
import com.swak.utils.AopTargetUtils;

/**
 * 一致性 后置处理器: 
 * 主要是识别 @Tcc 标注的属性，创建代理对象
 * 
 * @author lifeng
 * @date 2021年1月25日 下午1:26:28
 */
public class DubboReferencePostProcessor implements BeanPostProcessor {

	/**
	 * BeanName 必须符合Dubbo 规范才能启用
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		Class<?> clazz = bean.getClass();
		if (AopUtils.isAopProxy(bean)) {
			clazz = AopUtils.getTargetClass(bean);
		}

		// fill the reference
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {

			if (Modifier.isStatic(field.getModifiers())) {
				continue;
			}

			try {
				if (field.getAnnotation(Tcc.class) != null) {
					if (!field.canAccess(bean)) {
						field.setAccessible(true);
					}
					Object actualBean = AopTargetUtils.getTarget(bean);
					Object value = this.createProxy(field, field.get(actualBean));
					if (value != null) {
						field.set(actualBean, value);
					}
				}
			} catch (Exception e) {
				throw new BeanInitializationException("Failed to init Tcc reference at filed " + field.getName()
						+ " in class " + bean.getClass().getName(), e);
			}
		}
		return bean;
	}

	/**
	 * 创建 一致性 代理类
	 * 
	 * @param beanClass
	 * @param bean
	 * @return
	 * @throws Exception
	 */
	protected Object createProxy(Field field, Object bean) throws Exception {
		if (bean == null) {
			return null;
		}
		return ProxyFactory.newProxy(field, bean);
	}
}